import { Inject, Injectable } from '@nestjs/common';
import Iot, * as $Iot from '@alicloud/iot20180120';
import OpenApi, * as $OpenApi from '@alicloud/openapi-client';
import { IotConfig } from './type/IotCofig';
import { createHash } from 'node:crypto'


@Injectable()
export class AlicloudService {
	private client:Iot;

	public readonly config: Record<string, any>;

	constructor(
		@Inject('ALI_MODULE_OPTIONS') private options: IotConfig,
	) {
		this.iotClient()
	}

	private iotClient() {
		let config = new $OpenApi.Config({
			accessKeyId : this.options.accessKeyId,
			accessKeySecret : this.options.accessKeySecret,
			regionId : this.options.regionId
		});
		this.client = new Iot(config);
		
	}

	async registerDevice(productKey:string, deviceName:string) :Promise<$Iot.RegisterDeviceResponseBody>{
		try {
			const req = new $Iot.RegisterDeviceRequest({
				productKey,
				deviceName : deviceName,
			})


			if(this.options.iotInstanceId) {
				req.iotInstanceId = this.options.iotInstanceId
			}
			const r = await this.client.registerDevice(req)
	
			if(r.statusCode == 200) return r.body
		} catch (error) {
			
		}

		return null
	}

	async queryDeviceInfo(productKey:string, deviceName:string){

		try {
			const req = new $Iot.QueryDeviceInfoRequest({
				productKey,
				deviceName : deviceName,
			})
			if(this.options.iotInstanceId) {
				req.iotInstanceId = this.options.iotInstanceId
			}
			const r = await this.client.queryDeviceInfo(req)
	
			if(r.statusCode == 200) return r.body
		} catch (error) {
			
		}

		return null

	}

	async batchQueryDeviceDetail(productKey:string, deviceNames:string[]) {
		try {
			const req = new $Iot.BatchQueryDeviceDetailRequest({
				deviceName : deviceNames,
				productKey : productKey,
			})
			if(this.options.iotInstanceId) {
				req.iotInstanceId = this.options.iotInstanceId
			}
			const r = await this.client.batchQueryDeviceDetail(req)
	
			if(r.statusCode == 200) return r.body
		} catch (error) {
			
		}

		return null
	}

	async batchCheckDeviceNames(productKey:string, deviceNames:string[]) {
		try {
			const req = new $Iot.BatchCheckDeviceNamesRequest({
				productKey,
				deviceName : deviceNames,
			})
			if(this.options.iotInstanceId) {
				req.iotInstanceId = this.options.iotInstanceId
			}
			const r = await this.client.batchCheckDeviceNames(req)
	
			if(r.statusCode == 200) return r.body
		} catch (error) {
			
		}

		return null
	}

	async batchRegisterDeviceWithApplyId(productKey:string, applyId:number) {
		try {
			const req = new $Iot.BatchRegisterDeviceWithApplyIdRequest({
				productKey,
				applyId : applyId,
			})
			if(this.options.iotInstanceId) {
				req.iotInstanceId = this.options.iotInstanceId
			}
			const r = await this.client.batchRegisterDeviceWithApplyId(req)
	
			if(r.statusCode == 200) return r.body
		} catch (error) {
			
		}

		return null
	}

	async deleteDevice(productKey:string, deviceName:string) {
		try {
			const req = new $Iot.DeleteDeviceRequest({
                productKey : productKey,
                deviceName : deviceName
            })
			if(this.options.iotInstanceId) {
				req.iotInstanceId = this.options.iotInstanceId
			}
			const r = await this.client.deleteDevice(req)
	
			if(r.statusCode == 200) return r.body
		} catch (error) {
			
		}

		return null
	}

	async batchPub(productKey:string, topicTemplateName:string, deviceNames:string[], messageContent:{[key:string]:any}, options:{
		contentType?: string;
		correlationData?: string;
		deviceName?: string[];
		iotInstanceId?: string;
		messageContent?: string;
		messageExpiryInterval?: number;
		payloadFormatIndicator?: number;
		productKey?: string;
		qos?: number;
		responseTopicTemplateName?: string;
		retained?: boolean;
		topicShortName?: string;
		topicTemplateName?: string;
	} = {}) {
		try {
			const req = new $Iot.BatchPubRequest({
                productKey : productKey,
                deviceName : deviceNames,
				topicTemplateName : topicTemplateName,
				messageContent : Buffer.from(JSON.stringify(messageContent)).toString('base64'),
				...options
            })
			if(this.options.iotInstanceId) {
				req.iotInstanceId = this.options.iotInstanceId
			}

			const r = await this.client.batchPub(req)
	
			if(r.statusCode == 200) return r.body
		} catch (error) {
			
		}

		return null
	}


	async pub(productKey:string, topicFullName:string, deviceName:string, messageContent:{[key:string]:any}, options:{
		contentType?: string;
		correlationData?: string;
		deviceName?: string;
		iotInstanceId?: string;
		messageContent?: string;
		messageExpiryInterval?: number;
		payloadFormatIndicator?: number;
		productKey?: string;
		qos?: number;
		responseTopic?: string;
		retained?: boolean;
		topicAlias?: number;
		topicFullName?: string;
	} = {}) {
		try {

			if(!messageContent.msgId) {
				messageContent.msgId = createHash('md5').update(Date.now() + deviceName).digest('hex').toLowerCase().substring(0, 16)
			}else {
				if(messageContent.msgId.length < 16) {
					messageContent.msgId = messageContent.msgId.padStart(16, '0')
				}
			}



			const req = new $Iot.PubRequest({
                productKey : productKey,
                deviceName : deviceName,
				topicFullName : topicFullName,
				messageContent : Buffer.from(JSON.stringify(messageContent)).toString('base64'),
				...options
            })
			if(this.options.iotInstanceId) {
				req.iotInstanceId = this.options.iotInstanceId
			}

			const r = await this.client.pub(req)
	
			if(r.statusCode == 200) return r.body
		} catch (error) {
			
		}

		return null
	}

}
